<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="Cs2dV" id="Cs2dV"><span data-lake-id="uae2039da" id="uae2039da">典型回答</span></h1>
  <p data-lake-id="ua9dc6eb7" id="ua9dc6eb7"><span data-lake-id="u2dbbfac7" id="u2dbbfac7">​</span><br></p>
  <p data-lake-id="udccb8cd5" id="udccb8cd5"><span data-lake-id="u8cbf725e" id="u8cbf725e">Kafka提供的Producer和Consumer之间的消息传递保证语义有三种，所谓消息传递语义，其实就是Kafka的消息交付可靠保障，主要有以下三种：</span></p>
  <p data-lake-id="ub9735c14" id="ub9735c14"><span data-lake-id="ubbb87958" id="ubbb87958">​</span><br></p>
  <ul list="u2f963789">
   <li fid="u26223142" data-lake-id="ud1191522" id="ud1191522"><span data-lake-id="u6273ff5f" id="u6273ff5f">At most once—消息可能会丢，但绝不会重复传递；</span></li>
   <li fid="u26223142" data-lake-id="u43d6ea1b" id="u43d6ea1b"><span data-lake-id="ufacb5203" id="ufacb5203">At least once—消息绝不会丢，但可能会重复传递；</span></li>
   <li fid="u26223142" data-lake-id="ue4931656" id="ue4931656"><span data-lake-id="u291b607c" id="u291b607c">Exactly once—每条消息只会被精确地传递一次：既不会多，也不会少；</span></li>
  </ul>
  <p data-lake-id="uc2df2ec0" id="uc2df2ec0"><br></p>
  <p data-lake-id="ub11f2e5a" id="ub11f2e5a"><span data-lake-id="u27ee4f73" id="u27ee4f73">目前，Kafka 默认提供的交付可靠性保障是第二种，即At least once ，但是，其实依靠Kafka自身，是没有办法100%保证可靠性的。</span></p>
  <p data-lake-id="u78907232" id="u78907232"><br></p>
  <p data-lake-id="u47c619f0" id="u47c619f0"><br></p>
  <p data-lake-id="u8f1515ff" id="u8f1515ff"><span data-lake-id="ub4e4f78d" id="ub4e4f78d">上面的文档中，介绍了Kafka在保证消息的可靠性中做的一些努力，但是我们提到，</span><strong><span data-lake-id="ud8d78cee" id="ud8d78cee">Kafka只对已提交的消息做最大限度的持久化保证不丢失，但是没办法保证100%。</span></strong></p>
  <p data-lake-id="u3fb8e2e9" id="u3fb8e2e9"><strong><span data-lake-id="u08e01ea2" id="u08e01ea2">​</span></strong><br></p>
  <p data-lake-id="ua753b473" id="ua753b473"><strong><span data-lake-id="u0b809810" id="u0b809810">那么，整体分析下为什么吧。</span></strong></p>
  <p data-lake-id="u3244c22a" id="u3244c22a"><strong><span data-lake-id="ue5593bfe" id="ue5593bfe">​</span></strong><br></p>
  <h3 data-lake-id="HdO2P" id="HdO2P"><span data-lake-id="u6a36bf82" id="u6a36bf82">生产者</span></h3>
  <p data-lake-id="u0806d5e5" id="u0806d5e5"><br></p>
  <p data-lake-id="u5e8c3a9a" id="u5e8c3a9a"><span data-lake-id="uf0e1edee" id="uf0e1edee">Kafka允许生产者以异步方式发送消息，这意味着生产者在发送消息后不会等待确认。当然，我们可以注册一个回调等待消息的成功回调。</span></p>
  <p data-lake-id="uef179b99" id="uef179b99"><span data-lake-id="u504db017" id="u504db017">​</span><br></p>
  <p data-lake-id="u2732c460" id="u2732c460"><span data-lake-id="uf1ceeaaf" id="uf1ceeaaf">但是，如果生产者在发送消息之后，Kafka的集群发生故障或崩溃，而消息尚未被完全写入Kafka的日志中，那么这些消息可能会丢失。虽然后续有可能会重试，但是，如果重试也失败了呢？如果这个过程中刚好生产者也崩溃了呢？那就可能会导致没有人知道这个消息失败了，就导致不会重试了。</span></p>
  <p data-lake-id="uaa5c8377" id="uaa5c8377"><span data-lake-id="u4336ed02" id="u4336ed02">​</span><br></p>
  <h3 data-lake-id="olWOP" id="olWOP"><span data-lake-id="uafbf7b95" id="uafbf7b95">消费者</span></h3>
  <p data-lake-id="u3d9d8241" id="u3d9d8241"><br></p>
  <p data-lake-id="uf5a36e2a" id="uf5a36e2a"><span data-lake-id="u7f8f39d7" id="u7f8f39d7">消费者来说比较简单，只要保证在消息成功时，才提交偏移量就行了，这样就不会导致消息丢失了。</span></p>
  <p data-lake-id="u164fffa3" id="u164fffa3"><span data-lake-id="u0d16aad8" id="u0d16aad8">​</span><br></p>
  <h3 data-lake-id="PwLX1" id="PwLX1"><span data-lake-id="u176f21f2" id="u176f21f2">Broker</span></h3>
  <p data-lake-id="u0caadba2" id="u0caadba2"><br></p>
  <p data-lake-id="u550228ad" id="u550228ad"><span data-lake-id="ue00683cb" id="ue00683cb">Kafka使用日志来做消息的持久化的，日志文件是存储在磁盘之上的，但是如果Broker在消息尚未完全写入日志之前崩溃，那么这些消息可能会丢失了。</span></p>
  <p data-lake-id="ue5e464f2" id="ue5e464f2"><span data-lake-id="ud3766f01" id="ud3766f01">​</span><br></p>
  <p data-lake-id="u05a9dddb" id="u05a9dddb"><span data-lake-id="u7243b8ee" id="u7243b8ee">而且，操作系统在写磁盘之前，会先把数据写入Page Cache中，然后再由操作系统中自己决定什么时候同步到磁盘当中，而在这个过程中，如果还没来得及同步到磁盘中，就直接宕机了，那这个消息也就丢了。</span></p>
  <p data-lake-id="ua0ae3634" id="ua0ae3634"><br></p>
  <p data-lake-id="u95029529" id="u95029529"><span data-lake-id="ue2e5d9e0" id="ue2e5d9e0">当然，也可以通过配置</span><code data-lake-id="u6558afd3" id="u6558afd3"><span data-lake-id="u76e2fd52" id="u76e2fd52" style="color: rgb(18, 18, 18)">log.flush.interval.messages=1</span></code><span data-lake-id="u2678b57f" id="u2678b57f">，来实现类似于同步刷盘的功能，但是又回到了前面说的情况，还没来得及做持久化，就宕机了。</span></p>
  <p data-lake-id="ubdf8ad05" id="ubdf8ad05"><span data-lake-id="ud13b5255" id="ud13b5255">​</span><br></p>
  <p data-lake-id="ua4b2379f" id="ua4b2379f"><span data-lake-id="ub058a528" id="ub058a528">即使Kafka中引入了副本机制来提升消息的可靠性，但是如果发生同步延迟，还没来及的同步，主副本就挂掉了，那么消息就可能会发生丢失。</span></p>
  <p data-lake-id="ube78dfcc" id="ube78dfcc"><span data-lake-id="uad0b498b" id="uad0b498b">​</span><br></p>
  <p data-lake-id="u9e4e1396" id="u9e4e1396"><span data-lake-id="uc1789f0c" id="uc1789f0c">这几种情况，</span><strong><span data-lake-id="u091ba85a" id="u091ba85a">只从Broker的角度分析，Broker自身是没办法保证消息不丢失的</span></strong><span data-lake-id="u490cf993" id="u490cf993">，但是如果配合Producer，再配合</span><code data-lake-id="u1c69dafd" id="u1c69dafd"><span data-lake-id="u2319c300" id="u2319c300">request.required.acks = -1 </span></code><span data-lake-id="uf9203721" id="uf9203721">这种ACK策略，可以确保消息持久化成功之后，才会ACK给Producer，那么， 如果我们的Producer在一定时间段内，没有收到ACK，是可以重新发送的。</span></p>
  <p data-lake-id="u1cc115ff" id="u1cc115ff"><span data-lake-id="u614c1615" id="u614c1615">​</span><br></p>
  <p data-lake-id="u481f81d0" id="u481f81d0"><span data-lake-id="u762cb95c" id="u762cb95c">但是，这种重新发送，就又回到了我们前面介绍生产者的时候的问题，生产者也有可能挂，重新发送也有可能会没有发送依据，导致消息最终丢失。</span></p>
  <p data-lake-id="ub51aefb5" id="ub51aefb5"><span data-lake-id="u787a5339" id="u787a5339">​</span><br></p>
  <p data-lake-id="u0f791c2b" id="u0f791c2b"><strong><span data-lake-id="u2a1a2c9f" id="u2a1a2c9f">所以，我们说，只靠Kafka自己，其实是没有办法保证极端情况下的消息100%不丢失的。</span></strong></p>
  <p data-lake-id="ucf402880" id="ucf402880"><strong><span data-lake-id="u52b1c53c" id="u52b1c53c">​</span></strong><br></p>
  <p data-lake-id="u3485f94d" id="u3485f94d"><strong><span data-lake-id="u95e38d09" id="u95e38d09">但是，我们也可以在做一些机制来保证，比如引入分布式事务，或者引入本地消息表等，保证在Kafka Broker没有保存消息成功时，可以重新投递消息。这样才行。</span></strong></p>
  <p data-lake-id="ucda23900" id="ucda23900"><strong><span data-lake-id="uc44768a4" id="uc44768a4">​</span></strong><br></p>
 </body>
</html>